package com;

import jade.util.leap.ArrayList;
import gateway.ontology.classes.basic_translation.ParamNotFoundException;

import com.sun.squawk.util.StringTokenizer;

/**
 * This object is used to enable Agent's behavior execution. There a set of parameters:
 * SourceID: 	Event's source ID (an agent, the ExecutionEngine)
 * TargetID: 	Event's Target ID (an agent)
 * Name:		The name specifies Event's characteristics. A set of possible name are specified like Class Constants
 * Type:		There are 3 possible types, specified like Class Constants 
 * @author Francesco Aiello, Antonio Guerrieri, Raffaele Gravina
 *
 */
public class Event {
	
	/*
	 * MIGRATION EVENTS NAMES
	 */
	public static final byte MGR_EXECUTED 			= 1;
	public static final byte MGR_REQUEST 			= 2;
	public static final byte MGR_ACK 				= 3;
	
	/*
	 * TEMPERATURE EVENTS NAMES
	 */
	public static final byte TMP_CURRENT 			= 11;
	public static final byte TMP_THRESHOLDCHANGED 	= 12;
	public static final byte TMP_THRESHOLDEXCEDEED 	= 13;
	public static final byte TMP_TERMINATION 		= 14;
	public static final byte TMP_ID			 		= 15;
	public static final byte TMP_NOT_AVAILABLE 		= 16;
	
	/*
	 * LIGHT EVENTS NAMES
	 */
	public static final byte LGH_CURRENT 			= 21;
	public static final byte LGH_THRESHOLDCHANGED 	= 22;
	public static final byte LGH_THRESHOLDEXCEDEED 	= 23;
	public static final byte LGH_TERMINATION 		= 24;
	public static final byte LGH_ID			 		= 25;
	public static final byte LGH_NOT_AVAILABLE 		= 26;
	
	/*
	 * ACCELEROMETER EVENTS NAMES
	 */
	public static final byte ACC_CURRENT 			= 31;
	public static final byte ACC_CURRENT_ALL_AXES	= 32;
	public static final byte ACC_TILT	 			= 33;
	public static final byte ACC_THRESHOLDCHANGED 	= 34;
	public static final byte ACC_THRESHOLDEXCEDEED 	= 35;
	public static final byte ACC_TERMINATION 		= 36;
	public static final byte ACC_ID			 		= 37;
	public static final byte ACC_NOT_AVAILABLE 		= 38;

	/*
	 * MESSAGES EVENTS NAMES
	 */
	public static final byte MSG		 			= 41;
	public static final byte MSG_TO_BASESTATION		= 42;

	/*
	 * TIMER EVENTS NAMES
	 */
	public static final byte TMR_EXPIRED 			= 51;
	
	/*
	 * AGENT EVENTS
	 */
	public static final byte AGN_TERMINATED			= 61;
	public static final byte AGN_START				= 62;
	public static final byte AGN_CREATION			= 63;
	public static final byte AGN_ID					= 64;
	
	
	/*
	 * SPOT DISCOVERING
	 */
	public static final byte DSC_PUBLISH			= 71;
	public static final byte DSC_ANSWER				= 72;
	public static final byte DSC_REFRESH			= 73;
	
	/*
	 * EXECUTION ENGINE FUNCTIONS
	 */
	public static final byte EXE_GET_ALL_AGENTS		= 81;
	public static final byte EXE_GET_LOCAL_AGENTS	= 82;
	public static final byte EXE_GET_REMOTE_AGENTS	= 83;
	public static final byte EXE_GET_NEIGHBORS		= 84;
	
	/*
	 * LEDS
	 */
	public static final byte LED_ON					= 91;
	public static final byte LED_OFF				= 92;
	public static final byte LED_BLINK				= 93;
	
	/*
	 * SWITCH
	 */
	public static final byte SWT_PRESSED_RELEASED	= 101;
	public static final byte SWT_ID					= 102;
	public static final byte SWT_TERMINATION		= 103;
	
	/*
	 * FLASH
	 */
	public static final byte FLS_ADD				= 110;
	public static final byte FLS_DELETE				= 111;
	public static final byte FLS_SET				= 112;
	public static final byte FLS_GET				= 113;
	
	/*
	 * BATTERY
	 */
	public static final byte BTR_CURRENT_LEVEL				= 120;
	
	/*
	 * EVENT TYPE
	 */
	public static final byte NOW					= 1;
	public static final byte FIRST_OCCURRENCE		= 2;
	public static final byte PERMANENT				= 3;
	
	public static final String FIELDS_SEPARATOR		= "$";
	public static final String PARAMS_SEPARATOR		= "&";
	public static final String VECTOR_PARAM_SEPARATOR= "?";
	public static final String EMPTY_FIELD			= "null";

	private String sourceID = "";
	
	private String targetID = "";
	private byte name;
	
	/**
	 * The "params" structure is organized as follows: Key1->Value1->Key2->Value2->...
	 * Every Object within the Vector consists of a String.
	 * @uml.property name="params"
	 */
	private ArrayList params = null;
	
	private byte type;
	
	/**
	 * This constructor needs sourceID, targetID, name and type of the Event
	 * @param sourceID Event's source ID (an agent, the ExecutionEngine)
	 * @param targetID Event's Target ID (an agent)
	 * @param name The name specifies Event's characteristics. A set of possible name are specified like Class Constants
	 * @param type There are 3 possible types, specified in the Class Constants
	 */
	public Event(String sourceID, String targetID, byte name, byte type) {
		if (sourceID.equals(""))
			this.sourceID = EMPTY_FIELD;
		else
			this.sourceID = sourceID;
		
		if (targetID.equals(""))
			this.targetID = EMPTY_FIELD;
		else
			this.targetID = targetID;
		
		this.name = name;
		this.params = new ArrayList();
		this.type = type;
	}
	
	/**
	 * This constructor needs onlye name and type of the Event
	 * @param name The name specifies Event's characteristics. A set of possible name are specified like Class Constants
	 * @param type There are 3 possible types, specified in the Class Constants
	 */
	public Event(byte name, byte type) {
		this.sourceID = EMPTY_FIELD;
		this.targetID = EMPTY_FIELD;
		this.name = name;
		this.params = new ArrayList();	
		this.type = type;
	}

	/**
	 * This constructor can create an Event Object starting from a "String" representation of the event.
	 * It needs only a well formatted string  
	 * @param event String representation of the event
	 */
	public Event (String event) {
			StringTokenizer eventTokens = new StringTokenizer(event, FIELDS_SEPARATOR);
		if (eventTokens.hasMoreTokens()) 
			this.sourceID = eventTokens.nextToken();
		if (eventTokens.hasMoreTokens()) 
			this.targetID = eventTokens.nextToken();
		if (eventTokens.hasMoreTokens()) 
			this.name = Byte.parseByte(eventTokens.nextToken());
		if (eventTokens.hasMoreTokens()) 
			this.type = Byte.parseByte(eventTokens.nextToken());
		if (eventTokens.hasMoreTokens()) {
			StringTokenizer paramsTokens = new StringTokenizer(eventTokens.nextToken(), PARAMS_SEPARATOR);
			params = new ArrayList();
			while(paramsTokens.hasMoreTokens()) 				
				params.add(paramsTokens.nextToken());				
		}
	}
	
	public Event copy() {
		return new Event(this.toString());
	}
	
	/**
	 * Getter of the property <tt>name</tt>
	 * 
	 * @return Returns the name.
	 */
	public byte getName() {
		return name;
	}
	
	/**
	 * Getter of the property <tt>params</tt>
	 * The "params" structure is organized as follows: Key1->Value1->Key2->Value2->...
	 * Every Object within the Vector consists of a String.
	 * 
	 * @return Returns the params.
	 */
	public ArrayList getAllParams() {
		return params;
	}
	
	/**
	 * Returns the value for the given param-key
	 * 
	 * @return Returns the value for the given param-key or throws ParamNotFoundException if the param-key is not found.
	 */
	public String getParam(String key) throws ParamNotFoundException {
		/*
		 * Key1->Value1->Key2->Value2->... 
		 */
		int iKey = params.indexOf(key);
		if (iKey == -1)
			throw new ParamNotFoundException();
		else
			return (String)params.get(iKey + 1);
	}
	
	
	/**
	 * Set the value of the given key
	 * @param key Parameter's Name
	 * @param value Parameter's Value
	 */
	public void setParam(String key, String value) throws CharNotValidException {
		
		if (!this.IsStringValid(value))
			throw new CharNotValidException();
		int iKey = params.indexOf(key);
		if (iKey == -1) {
			params.add(key);
			params.add(value);
		}
		else {
			params.add(iKey+1, value);
			params.remove(iKey+2); // remove old value;
		}
	}
	
	/**
	 * Getter of the property <tt>type</tt>
	 * 
	 * @return the type.
	 */
	public byte getType() {
		return type;
	}
	
	/**
	 * Setter of the property <tt>type</tt>
	 * 
	 * @param type
	 *            The type to set.
	 */
	public void setType(byte type) {
		this.type = type;
	}

	/**
	 * Getter of the property <tt>sourceID</tt>
	 * 
	 * @return the sourceID
	 */
	public String getSourceID() {
		return sourceID;
	}

	/**
	 * Getter of the property <tt>targetID</tt>
	 * 
	 * @return the targetID
	 */
	public String getTargetID() {
		return targetID;
	}

	/**
	 * Provides a "String" representation of the event. The string is formatted using some separator characters
	 * 
	 * @return a formatted String
	 */
	public String toString() {
		
		String event = 	this.sourceID 	+ FIELDS_SEPARATOR + 
						this.targetID	+ FIELDS_SEPARATOR +
						this.name		+ FIELDS_SEPARATOR +
						this.type		+ FIELDS_SEPARATOR;
		for (int i = 0; i < params.size(); i++) 
			event += params.get(i) + PARAMS_SEPARATOR;
		return event;
	}
	
	/**
	 * Checks if the paramater's name and value have disallowed parameters 
	 * @param string paramater's name and value
	 * @return true if the string is valid, false otherwise
	 */
	private boolean IsStringValid (String string) {
		if (	string.indexOf(FIELDS_SEPARATOR) != -1 ||
				string.indexOf(PARAMS_SEPARATOR) != -1 ||
				string.indexOf(VECTOR_PARAM_SEPARATOR) != -1
				)
			return false;
		else
			return true;
	}
	
}
